版本控制系统(VCS)
三大要素:版本控制、主动提交、中央仓库。
每个团队成员向中央仓库主动提交自己的改动和同步别人的改动,并在需要的时候查看和操作历史版本,这就是版本控制系统。
中央式版本控制系统(Centralized VCS)
- 本地没有仓库,只有一份你签出的代码和最基本的版本信息(服务器位置以及一些关于版本号的缓存等),断网则什么都干不了,如果中央服务器出了问题,所有人都没法干活了。
分布式版本控制系统(Distributed VCS)
每个团队成员都有带版本管理的本地仓库,有所有人提交的版本记录,可以离线对仓库做一些操作,有网络时再推送到远程仓库即可。它的中央仓库虽然也保存了历史版本,但这份历史版本更多的是作为团队间的同步中转站。
大多数的操作可以在本地进行,所以速度更快,而且由于无需联网,所以即使不在公司甚至没有在联网,你也可以提交代码、查看历史,从而极大地减小了开发者的网络条件和物理位置的限制。
由于可以提交到本地,所以你可以分步提交代码,把代码提交做得更细,而不是一个提交包含很多代码,难以 review 也难以回溯。因为每个人电脑里都有完整的版本库,某个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。
Git 的一些概念
本地仓库 (Local Repository)
工作区有一个隐藏目录 .git ,这个不算工作区,而是 Git 的版本库。也叫仓库(repository),你可以简单理解成一个目录,这个目录里面的所有文件都可以被 Git 管理起来,每个文件的修改和删除都能被跟踪,以便任何时刻都可以追踪历史。
工作区(Working Directory)
就是你在电脑里能看到的目录,它保存了你当前从仓库中签出(checkout)的内容。
暂存区(Stage Area)
一个汇集所有待提交的文件改动的地方。是 .git 目录下一个叫做 index 的文件,你通过 add 指令暂存的内容都会被写进这个文件里。
偏移符号
^ :在 commit 的后面加一个或多个 ^ 号,可以把 commit 往回偏移,偏移的数量是 ^ 的数量。例如:master^ 表示 master 指向的 commit 之前的那个 commit。HEAD^^ 表示 HEAD 所指向的 commit 往前数两个 commit 。
~ :在 commit 的后面加上 ~ 号和一个数,可以把 commit 往回偏移,偏移的数量是 ~ 号后面的数。例如:HEAD~5 表示 HEAD 指向的 commit 往前数 5 个 commit 。
HEAD
当前
commit在哪里,HEAD就在哪里,这是一个永远自动指向当前commit的引用,所以你永远可以用HEAD来操作当前commit。使用
checkout、reset等指令手动改变当前commit的时候,HEAD也会一起跟过去。每次当有新的commit的时候,工作目录会自动与最新的commit对应,HEAD也会转而指向最新的commit。HEAD是Git中一个独特的引用,它是唯一的。HEAD除了可以指向commit,还可以指向一个branch,当它指向某个branch的时候,会通过这个branch来间接地指向某个commit。另外,当HEAD在提交时自动向前移动的时候,它会像一个拖钩一样带着它所指向的branch一起移动。
branch
是一种引用,其中
master是Git的默认branch(俗称主branch/ 主分支)。新创建的仓库是没有任何commit的,但在它创建第一个commit时,会把master指向它,并把HEAD指向master。当使用
git clone时,除了从远程仓库把.git这个仓库目录下载到工作目录中,还会checkout(签出)master(checkout的意思就是把某个commit作为当前commit,把HEAD移动过去,并把工作目录的文件内容替换成这个commit所对应的内容)。所以刚clone的代码默认是处于master分支的最新提交位置内容。branch包含了从初始commit到它的所有路径,而不是一条路径。并且,这些路径之间是彼此平等的。
注意:
由于 Git 中的 branch 只是一个引用,所以删除 branch 的操作也只会删掉这个引用,并不会删除任何的 commit 。(不过如果一个 commit 不在任何一个 branch 的「路径」上,或者换句话说,如果没有任何一个 branch 可以回溯到这条 commit,那么在一定时间后,它会被 Git 的回收机制删除掉。)
比如:有分㕚的不同分支,其中的一个分支有提交过,但是从来没被合并,但是分支引用却被删除了,那么这些还没被合并的 commits 将会在一定时间后被回收掉。
Git 基本命令
clone
用于将远程仓库取下来,要克隆一个仓库,首先必须知道仓库的地址,然后使用
git clone指令。Git支持多种协议,包括https,但通过ssh支持的原生git协议速度最快。而且,把项目clone下来时不用https协议,而是用ssh协议,就不需要每次操作都要输入用户名和密码了。可以加一个额外参数来手动指定本地仓库的根目录名称。
git-practice-another 为指定目录名称
git clone git@github.com:fandazeng/git-practice.git git-practice-another
关联本地仓库和远程仓库
要关联一个远程库,使用指令
git remote add origin git@server-name:path/repo-name.git。然后,使用指令
git push -u origin master来推送master分支的所有内容(第一次推送才需要用到-u将本地的master分支和远程的master分支关联起来)。此后,本地提交后就可以使用指令
git push origin master推送最新修改。
init
初始化一个 Git 仓库,使用 git init 。
add
将具体的文件改动添加进暂存区(不是文件名),在 add 之后的新改动并不会自动被添加进暂存区。
git add -A:提交所有变化。git add -u:提交被修改和被删除文件,不包括新文件。git add .:提交新文件和被修改文件,不包括被删除文件。
commit
将暂存区的内容提交到仓库。 commit 的时候会进入到编辑界面:
i (插入模式) - esc (变回命令模式) - 大写两次 ZZ 退出并保存 )。
可以通过
-m直接在后面添加本次提交的说明,这样就不会进入编辑模式了。有时候,提交了之后才发现改错了,又不想增加一个
commit,可以在提交时加上--amend参数,Git说不会在当前commit上增加commit,而是会把当前commit里的内容和暂存区里的内容合并起来后创建一个新的commit,用这个新的commit把当前commit替换掉。git commit -m "reset last line" --amend在
android studio上,可以在提交界面右边的Amend commit打勾。
status
用来查看工作目录当前状态的指令,可以知道哪些文件被 add 过,哪些文件被修改过等等。
log
该命令会列出你的提交历史。在 android studio 的命令行使用 git log 会处于特殊状态,可以通过双击大写的 Z 来退出。
git log -p:查看具体commit的改动细节。git log --stat:查看简要统计,没有-p那么深入细节。git log --graph:查看分支合并图,再加上--pretty=oneline --abbrev-commit参数,可以更简洁。
show
git show: 查看当前commit的改动内容。git show <commitId>: 查看任意commitId的改动内容。git show <tagName>: 查看tag(tag和commitId是绑定的)的改动内容。git show <commitId> <fileName>: 查看指定commit中的指定文件。
diff
git diff: 显示工作区和暂存区之间的不同,「如果你现在把所有文件都add,你会向暂存区中增加哪些内容」。git diff --staged/--cached: 显示暂存区和上一条提交之间的不同,[如果你立即commit,你将会提交什么」。git diff HEAD: 显示工作目录和上一条提交之间的不同,它是上面二者的内容相加。[如果你现在把所有文件都add然后commit,你将会提交什么」。
注意:从来没有被 add 过 的文件并不会显示出来,因此 Git 没跟踪它们,识别不出来。
branch
git branch:查看分支。git branch <name>:创建分支。git checkout <name>或者git switch <name>:切换分支。git checkout -b <name>或者git switch -c <name>:创建 + 切换分支。git merge <name>:合并某分支到当前分支。git branch -d <name>或git branch -D <name>:删除分支。git push origin -d <name>:删除远程分支。
注意:
出于安全考虑,没有被合并过的 branch 在删除时会失败(因为怕你误删掉「未完成」的 branch ),如果你确定是要删除这个 branch (例如某个未完成的功能被团队确认永久毙掉了),可以把 -d 改成 -D,就能删除了。
stash
用于临时存放工作目录的改动。
常用场景:
你在某个分支上工作,但功能还没完成,不想提交,又必须切换到其他分支进行版本发布或 bug 修复,就可以使用 git stash 来把工作现场保存起来,之后到切换回该分支进行恢复 git stash pop 或 git stash apply 。
注意:
没有被跟踪的文件(即从来没有被
add过的文件不会被stash起来,因为Git会忽略它们。如果想把这些文件也一起stash,可以加上-u参数,它是--include-untracked的简写。git stash -u每次调用
stash都会有一条记录,可以用git stash list查看所有的stash记录,记录中有索引。通过
git stash pop <索引>或者stash apply <索引>就可以使用对应的stash。apply可以保留stash空间,pop用于恢复的同时把stash内容也删除。为了避免pop完产生奇怪的问题,所以优先使用apply而不是pop。
tag
发布一个版本时,我们通常先在版本库中打一个标签,这样就唯一确定了打标签时刻的版本。将来想取指定标签的版本,就是把那个打标签的时刻的历史版本取出来。 tag 就是一个让人容易记住的有意义的名字,它跟某个 commit 绑在一起。标签是指向 commit 的死指针,分支是指向 commit 的活指针。
git tag <tagname>:新建一个标签,默认为HEAD,也可以指定commit。git tag -a <tagname> -m "说明信息":新建一个带说明信息的标签。git tag:查看所有标签。git push origin <tagname>:推送一个本地标签。git push origin --tags:推送全部未推送过的本地标签。git tag -d <tagname>:删除一个本地标签。git push origin :refs/tags/<tagname>:删除一个远程标签。git show <tagname>: 查看标签详细信息:
注意:
标签总是和某个
commit挂钩。如果这个commit都出现在不同分支,那么在这些分支上都可以看到这个标签。默认情况下,
git push命令并不会上传标签到远程仓库,必须显式地推送标签到服务器上。 这个过程就像共享远程分支一样。 (在studio上,需要在push界面给tags选项打勾。)git push origin [tagname]。git tag和git stash list都分别列出所有分支下的所有tag或stash,而不管你此刻处于哪个分支。这样做是为了使用便利性,你可以在任意分支中选择想要的tag或stash进行处理,而不用记得指定分支下有哪些tag或stash。
push
把当前的分支上传到远程仓库,并把分支路径上的所有 commit 也一并上传。
常用场景:
push 到远程的内容有问题,但分支是独立开发的,不会影响到其他人,那可以用 --amend 或 reset 把写错的 commit 修改或者删除掉,然后再 push 上去就好了。不过由于你在本地对已有的 commit 做了修改,这时你再 push 就会失败,因为中央仓库包含本地没有的 commit。但这个冲突不是因为同事 push 了新的提交,而是因为你刻意修改了一些内容,这个冲突是你预料到的,这时要选择强行 push :git push origin branch1 -f
注意:
默认情况下,你用不加参数的
git push只能上传那些之前从远端clone下来或者pull下来的分支,而如果需要push你本地自己创建的分支,则需要手动指定目标仓库和目标分支(并且目标分支的名称必须和本地分支完全相同)。push只上传当前分支,并不会上传HEAD,远程仓库的HEAD是永远指向默认分支(即master)的。也可以让远程仓库的分支名称跟本地的不一样,(其实是将本地的提交推送到远程已有的指定分支)。
git push<远程主机名> <本地分支名> : <远程分支名>。git push origin testBranch:testgit push origin <分支名> --force是强制让你本地的commit覆盖远程的commit,要慎用。有时候在
studio上commit时没有记录,是因为已经用命令commit了,可以直接push到远程。
fetch
将远程仓库的最新内容拉到本地,用户在检查了以后决定是否合并到工作分支中。
git fetch:将远程主机的更新全部取回本地 。git fetch <远程主机名> <分支名>:取回指定分支的更新,比如:git fetch origin master。
取回更新后,会返回一个 FETCH_HEAD ,指的是某个 branch 在服务器上的最新状态,我们可以在本地通过它查看刚取回的更新信息: git log -p FETCH_HEAD 。
merge
定义: 从目标 commit 和当前 commit (即 HEAD 所指向的 commit)分叉的位置起,把目标 commit 的路径上的所有 commit 的内容一并应用到当前 commit,然后自动生成一个新的 commit。(即合并自己没有的 commit)。
冲突: 当合并不同分支时,没有修改同一部分内容,算法会自动完成。如果两个分支修改了同一部分内容,merge 的自动算法就搞不定了。这种情况称为冲突(Conflict)。
常用命令:
git merge branch: 将branch合并到当前分支。git merge --abort,取消合并操作。合并的时候发生冲突,会处于一种待解决的中间状态,此时可以取消操作。
注意:
如果
HEAD和目标commit不存在分叉,但HEAD落后于目标commit,那么Git会直接把HEAD(以及它所指向的branch)移动到目标commit。这种操作叫做fast-forward(快速前移)。经典使用场景:本地的master没有新提交,而远端仓库中有同事提交了新内容到master。合并分支时,加上
--no-ff 参数就可以禁止使用fast forward模式合并git merge --no-ff -m "merge dev no-ff" dev,因为合并后的历史有分叉,就能看出来曾经做过合并,而fast forward方式看不出来曾经做过合并。
pull
将远程仓库的最新内容拉下来后直接合并,即: git pull = git fetch + git merge ,这样可能会产生冲突,需要手动解决。比如: git pull origin master 。
pull 的过程可以理解为:
git fetch origin master //从远程仓库的master分支拉取最新内容
git merge FETCH_HEAD //将拉取下来的最新内容合并到当前分支
reset
常用场景:
- 想把之前的
commit丢弃,回到某个commit的位置。比如某个修改提交导致整个项目编译不了,想要撤销提交,可以用git reset --hard 目标commit(SHA1 值)回到某个提交位置。
常用命令:
git reset --hard HEAD^:HEAD^表示你要恢复到哪个commit。因为你要撤销最新的一个commit,所以你需要恢复到它的父commit,也就是HEAD^。那么在这行之后,你的最新一条commit就被撤销了。
reset 的本质:
reset的本质是移动HEAD以及它所指向的branch到某一个commit上,它的实质行为并不是撤销。也就是说,在版本回退之后,之前的commit还在,可以使用git reflog查看原先的commit SHA-1码,然后git reset --hard commitid回退到刚才那个commit版本。
reset 的参数分析:
reset 指令可以重置 HEAD 和 branch 的位置,不过在重置它们的同时,对工作目录可以选择不同的操作,而对工作目录的操作的不同,就是通过 reset 后面跟的参数来确定的。
git reset --soft HEAD^,首先移动HEAD的指向,本质是撤销了上一次commit命令,但是保留着add命令。因此,上一次提交的内容会重新回到暂存区。因为当前的工作目录是在上一次提交的基础上的,所以工作目录内容不会有任何变动。此时你可以修改代码,再次add并commit,就能实现git commit --amend所要做的事情了。git reset (--mixed) HEAD^,首先移动HEAD的指向,然后清空暂存区所有的内容。本质撤销了上一次的git add和git commit命令。因为当前的工作目录是在上一次提交的基础上的,所以工作目录内容也不会有任何变动。这种方式比较安全,会保留工作目录的改动。git reset --hard HEAD^,首先移动HEAD的指向,然后清空暂存区所有的内容,最后清空了工作目录中所有的改动。此时就将HEAD指向的目标commit的内容跟暂存区的内容和工作目录的内容统一了,也就是working clean了。这种方式会比较危险,它会把当前工作的内容丢掉。
checkout
checkout 并不止可以切换分支 。git checkout branch名 的本质,其实是把 HEAD 指向指定的 branch,然后签出这个 branch 所对应的 commit 的工作目录。所以 checkout 的目标也可以不是 branch ,而是某个 commit 。
git checkout HEAD^^
git checkout master~5
git checkout 78a4bc
git checkout 78a4bc^
上面这些操作都是可以的。也可以 checkout 文件 来达到撤销本地改动的目的(删除在 git 里面也是修改操作)。
场景一:在工作区修改了某个文件,还没有 add ,突然想撤销了,希望恢复到版本一开始的状态。
vi readme.txt,修改了一些内容。git restore readme.txt或使用git checkout -- readme.txt来直接撤销所有的修改。
场景二: 新建了一个文件,并 commit 到了仓库。然后在本地不小心删除了该文件,想要恢复回来。
rm test.txt,在本地把test.txt文件删除掉。git checkout -- test.txt,重新签出文件即可恢复 。
注意:
reset会移动HEAD分支的指向,而checkout只会移动HEAD自身来指向另一个分支(checkout是带着HEAD走,reset是带着HEAD和branch一起走)。例如,假设我们有master和develop分支,我们现在在develop上(所以HEAD指向它)。 如果我们运行git reset master,那么develop自身现在会和master指向同一个提交。 而如果我们运行git checkout master的话,develop不会移动,HEAD自身会移动。 现在HEAD将会指向master。
revert
撤销某个 commit ,比如说某个 commit 新增了一些内容,而此时这些内容不需要了,那么有人可能会想到用 amend 来修改之前的 commit ,这样也可以。但是,如果新增的内容比较多,你要把所有的内容删掉再用 amend 提交就非常麻烦了。应该用 revert ,直接撤销指定 commit 的所有改动。
git revert commitID : 撤销指定的 commitID 。
git revert HEAD^^ :revert 的是倒数第二次提交 。
git revert HEAD : 反转最后一次提交。
注意:
revert是强行在你的commits链条上去除某一个环节(即让某一个提交的内容去除了),因此不是非常确定对后续的commit没有任何影响的话,最好还是不要乱用。在
revert完成之后,把新的commit再push上去,这个commit的内容就被撤销了。它和前面所介绍的撤销方式相比,最主要的区别是,这次改动只是被「反转」了,并没有在历史中消失掉,你的历史中会存在两条commit:一个原始commit,一个对它的反转commit。如果反转的提交后悔了,可以再次把反转的提交再反转或撤销。git reset --hard HEAD^是撤销当前commit,git revert HEAD是反转最后一次提交 ,注意两者的区别。reset其实是回滚到指定的分支,所以用HEAD^,即回到倒数第二提交,也就是最后最新的提交去掉了。可以用
rebase -i的方式执行drop指令来丢弃某个提交。
reflog
用来查看 Git 仓库中的引用的移动记录。默认查看 HEAD 的移动历史,除此之外,也可以手动加上名称来查看其他引用的移动记录,git reflog master 查看 master 的移动记录。
常用场景:我在某个 commit 中删除了某个分支,在以后想要恢复。
可以使用
git reflog查看删除分支的移动记录,最后找到对应的commitId。签出对应
commitId的版本,然后再创建分支,这个分支就是之前被删除的分支。比如:git checkout c08de9a,git checkout -b branch1。再签回
Head的版本,就回到之前的版本了。
注意:不再被引用直接或间接指向的 commits 会在一定时间后被 Git 回收,所以使用 reflog 来找回删除的 branch 的操作一定要及时,不然有可能会由于 commit 被回收而再也找不回来。
cherry-pick
在 master 分支上修复的 bug ,想要合并到当前 dev 分支,可以用 git cherry-pick <commit> 命令,把 bug 提交的修改“复制”到当前分支,避免重复劳动。
rebase
使用场景一:
git rebase 目标基础点 。
rebase 的意思是,给你的 commit 序列重新设置基础点(也就是父 commit)。展开来说就是,把你指定的 commit 以及它所在的 commit 串,以指定的目标 commit 为基础,依次重新提交一次。当两个不同的分支都有提交的话,此时将它们 merge 就会出现分叉的情况,如果不想出现这样的情况,可以不用 merge ,而用 rebase 。比如:
git checkout branch1
git rebase master
这两句命令的效果就是,以 master 的 HEAD 为基点,把 branch1 的提交全部移到 master 上。另外,在 rebase 之后,记得切回 master 再 merge 一下,把 master 移到最新的 commit 。
git checkout master
git merge branch1
需要说明的是,rebase 是站在需要被 rebase 的 commit 上进行操作,这点和 merge 是不同的。
使用场景二:
rebase -i:交互式 rebase
比如,我们要修改某一个 commit ,不是最新的哦(修改最新的可以用 commit --amend),所谓「交互式 rebase」,就是在 rebase 的操作执行之前,你可以指定要 rebase 的 commit 链中的每一个 commit 是否需要进一步修改。
比如 : git rebase -i HEAD^^ (加上 -i 就是变为交互式的)。如果没有 -i 参数的话,这种「原地 rebase」相当于空操作,会直接结束。而在加了 -i 后,就会跳到一个新的界面。然后,选择对应的操作,操作的命令有如下种类:
pick:保留该commit(缩写:p)
reword:保留该commit,但我需要修改该commit的注释(缩写:r)
edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e)
squash:将该commit和前一个commit合并(缩写:s)
fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f)
exec:执行shell命令(缩写:x)
drop:我要丢弃该commit(缩写:d),可以用这个命令来删除之前的指定 commit ,在
Android studio 上是通过 skip 选项来移除的。
假如我们改成了 edit 并退出,此时就会进入修正状态,此时可以 add 和 commit ,在修复完成之后,就可以用rebase --continue 来继续 rebase 过程,把后面的 commit 直接应用上去。
git rebase --continue
小结:
- 使用方式是
git rebase -i 目标commit; - 在编辑界面中指定需要操作的
commits以及操作类型; - 操作完成之后用
git rebase --continue来继续rebase过程。
添加文件到 Git 仓库
分两步:
使用命令
git add <file>,可反复多次使用来添加多个文件;使用命令
git commit -m <message>;
文件状态
每个文件都有 "changed / unstaged"(已修改)" ,"staged"(已修改并暂存) ,"commited"(已提交) 三种状态,以及一种特殊状态 "untracked"(未跟踪)。
详解:
- 文件从来没被
add过,是处于特殊状态"untracked"(未跟踪)。 - 文件被
add了,但并没commit,处于"staged"(已修改并暂存)。 - 文件被
commit了 ,处于"commited"(已提交)。 - 文件曾经被
add过并提交过,此时文件被修改了,处于"changed / unstaged"(已修改),此时文件需要再次被add,就会处于状态 2,被commit后就会处于状态 3 。
引用的本质
所谓「引用」(
reference),其实就是一个个的字符串。这个字符串可以是一个commit的SHA-1码(例:c08de9a…),也可以是一个branch(例:ref: refs/heads/feature3)。Git中的HEAD和每一个branch以及其他的引用,都是以文本文件的形式存储在本地仓库.git目录中,而Git在工作的时候,就是通过这些文本文件的内容来判断这些所谓的「引用」是指向谁的。
分支策略
首先,master 分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活。干活都在 dev 分支上,dev 分支是不稳定的,到某个时候,比如 1.0 版本发布时,再把 dev 分支合并到 master 上,在 master 分支发布 1.0 版本;每个人都在 dev 分支上干活,每个人都有自己的分支,时不时地往 dev 分支上合并就可以了。
feature 分支
每添加一个新功能,最好新建一个 feature 分支,在上面开发,完成后,合并,最后,删除该 feature 分支。这样做有两个好处:代码分享和一人多任务。一人多任务的场景非常简单,根本分开的功能来创建不同的 feature 分支来开发就是多任务。
代码分享使用场景:
- 分开一个新功能,新建一个
feature分支:git switch -c feature。 - 提交了很多代码并推送到远程:
git push origin feature。 - 你的同事通过
fetch或pull把你的代码拉下来,并切换到你的功能分支。 - 这时候,你的同事就可以帮你
review代码,如果有问题,你就反复修改即可。 - 没有问题即可合并到
master分支了,切换到master并pull一下来更新最新代码,然后merge feature分支。 - 最后,删除本地和远程分支:
git branch -d feature ,git push origin -d feature。
注意:如果发现 feature 分支代码太烂,不准备再要了,就可以直接删除本地和远程分支,非常方便,由于分支还没有被合并,如果删除,将丢失掉修改,如果要强行删除,需要使用大写的 -D 参数。
多人协作
当你从远程仓库克隆时,实际上 Git 自动把本地的 master 分支和远程的 master 分支对应起来了,远程仓库的默认名称是 origin 。
要查看远程库的信息,用 git remote ,加上参数 -v 显示更详细的信息。
本地新建的分支如果不推送到远程,对其他人就是不可见的;
从本地推送分支,使用 git push origin branch-name ,如果推送失败,先用 git pull 抓取远程的新提交;
在本地创建和远程分支对应的分支,使用 git checkout -b branch-name origin/branch-name ,本地和远程分支的名称最好一致;
建立本地分支和远程分支的关联,使用 git branch --set-upstream branch-name origin/branch-name;
从远程抓取分支,使用 git pull ,如果有冲突,要先处理冲突。
配置别名
--global 参数是全局参数,也就是这些命令在这台电脑的所有 Git 仓库下都有用,针对当前用户。如果不加,那只针对当前的仓库起作用。
git config --global alias.st status (为查看状态配置别名)
git config --global alias.last 'log -1' (为显示最后一次提交信息配置别名)
git config --global alias.unstage 'reset HEAD' (为暂存区的修改撤销回工作区配置别名)
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
那么配置文件放在哪里呢:
- 每个仓库的
Git配置文件都放在.git/config文件中 - 当前用户的
Git配置文件放在用户主目录下的一个隐藏文件.gitconfig中 - 我们可以直接在配置文件中进行配置
基本命令操作步骤演示
创建仓库
mkdir git-learning,创建一个名为git-learning的文件夹。cd git-learning,切换到该文件夹目录。pwd,该命令可以显示当前目录,比如(/e/2020_mine_repo/git-learning)。git init,把这个目录变成Git可以管理的仓库,目录下会多了一个.git目录,该目录是Git来跟踪管理版本库的。
把文件添加到版本库
vi readme.txt,然后进入编辑界面,点击i变成插入模式,写完内容后双击大写Z,就能创建一个文件,文件一定要在版本库内。git add readme.txt,把文件添加到暂存区。git commit -m "wrote a readme file",把文件提交到仓库。
修改文件再添加到版本库
vi readme.txt,更改内容。- 用
git status看看仓库当前的状态。 - 用
git diff readme.txt,查看一下修改了什么内容。 - 用
git add readme.txt,添加到暂存区。 - 用
git status再看看仓库当前的状态。 - 用
git commit -m "add distributed",提交到仓库。 - 用
git status再看看仓库当前的状态。
对文件多次修改的操作演示
vi readme.txt,编辑内容。cat readme.txt,看一下内容。git add readme.txt,添加到暂存区。git status,查看一下状态。vi readme.txt,再编辑一下内容。git commit -m "git tracks changes",提交到仓库。git status,查看一下状态。git diff HEAD -- readme.txt,查看当前文件和仓库里最新版本的区别。- 再次
add和commit第二次修改的内容。
查看文件状态的操作演示
vi readme.txt,修改内容。vi LICENSE.txt,新增一个文件。ls -ah,可以查看一下所有的文件,-ah是把隐藏文件都查看,默认不查看。git status,查看一下状态。git add .,该指令可以把所有的修改一次性add到暂存区。git status再查看一下文件状态。git commit -m "understand how stage works",提交到仓库。git status再查看一下文件状态。此时nothing to commit, working tree clean。
版本回退
vi readme.txt,更改内容,然后add和commit。git log,查看历史记录,也可以用git log --pretty=oneline来输出更少的内容。git reset --hard HEAD^,回退到上一个版本。cat readme.txt,该命令可以查看一下文件的内容,回退到了上一版本的内容。- 再用
log命令查看一下历史记录,只能看到当前的版本和以前的版本信息,最新的信息看不到了。 - 如果再想回到之前的版本,可以用
reflog命令,来查看一下仓库的移动记录,默认是查看HEAD的。 - 找到之前的
commitId(比如是9360cd0) ,然后git reset --hard 9360cd0,回到之前的版本。
小结:
HEAD 指向的版本就是当前版本,因此,Git 允许我们在版本的历史之间穿梭,使用命令 git reset --hard commit_id 。穿梭前,用git log 可以查看提交历史,以便确定要回退到哪个版本。要重返未来,用 git reflog 查看命令历史,以便确定要回到未来的哪个版本。